package edu.northwestern.cbits.purple_robot_manager.probes.builtin; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.PackageManager; import android.net.wifi.ScanResult; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.Bundle; import android.preference.CheckBoxPreference; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.support.v4.content.ContextCompat; import edu.northwestern.cbits.purple_robot_manager.R; import edu.northwestern.cbits.purple_robot_manager.activities.settings.FlexibleListPreference; import edu.northwestern.cbits.purple_robot_manager.logging.LogManager; import edu.northwestern.cbits.purple_robot_manager.logging.SanityCheck; import edu.northwestern.cbits.purple_robot_manager.logging.SanityManager; import edu.northwestern.cbits.purple_robot_manager.probes.Probe; public class WifiAccessPointsProbe extends Probe { private static final String ACCESS_POINT_COUNT = "ACCESS_POINT_COUNT"; protected static final String ACCESS_POINTS = "ACCESS_POINTS"; protected static final String BSSID = "BSSID"; protected static final String SSID = "SSID"; protected static final String CAPABILITIES = "CAPABILITIES"; protected static final String SCAN_FREQUENCY = "FREQUENCY"; protected static final String LEVEL = "LEVEL"; protected static final String CURRENT_SSID = "CURRENT_SSID"; protected static final String CURRENT_RSSI = "CURRENT_RSSI"; protected static final String CURRENT_BSSID = "CURRENT_BSSID"; protected static final String CURRENT_LINK_SPEED = "CURRENT_LINK_SPEED"; private static final boolean DEFAULT_ENABLED = true; private static final String ENABLED = "config_probe_wifi_enabled"; private static final String FREQUENCY = "config_probe_wifi_frequency"; private long _lastCheck = 0; private BroadcastReceiver _receiver = null; @Override public String getPreferenceKey() { return "built_in_wifi"; } @Override public String name(Context context) { return "edu.northwestern.cbits.purple_robot_manager.probes.builtin.WifiAccessPointsProbe"; } @Override public String title(Context context) { return context.getString(R.string.title_wifi_probe); } @Override public String probeCategory(Context context) { return context.getResources().getString(R.string.probe_other_devices_category); } @Override public boolean isEnabled(Context context) { if (super.isEnabled(context)) { final SharedPreferences prefs = Probe.getPreferences(context); if (prefs.getBoolean(WifiAccessPointsProbe.ENABLED, WifiAccessPointsProbe.DEFAULT_ENABLED)) { if (ContextCompat.checkSelfPermission(context, "android.permission.ACCESS_FINE_LOCATION") == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(context, "android.permission.ACCESS_COARSE_LOCATION") == PackageManager.PERMISSION_GRANTED) { final WifiAccessPointsProbe me = this; if (this._receiver == null) { this._receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (prefs.getBoolean(WifiAccessPointsProbe.ENABLED, WifiAccessPointsProbe.DEFAULT_ENABLED) == false) return; if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) { WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); List<ScanResult> results = wifi.getScanResults(); Bundle bundle = new Bundle(); bundle.putString("PROBE", me.name(context)); bundle.putLong("TIMESTAMP", System.currentTimeMillis() / 1000); ArrayList<Bundle> accessPoints = new ArrayList<>(); if (results != null) { bundle.putInt(WifiAccessPointsProbe.ACCESS_POINT_COUNT, results.size()); for (ScanResult result : results) { Bundle pointBundle = new Bundle(); pointBundle.putString(WifiAccessPointsProbe.BSSID, result.BSSID); pointBundle.putString(WifiAccessPointsProbe.SSID, result.SSID); pointBundle.putString(WifiAccessPointsProbe.CAPABILITIES, result.capabilities); pointBundle.putInt(WifiAccessPointsProbe.SCAN_FREQUENCY, result.frequency); pointBundle.putInt(WifiAccessPointsProbe.LEVEL, result.level); accessPoints.add(pointBundle); } } else bundle.putInt(WifiAccessPointsProbe.ACCESS_POINT_COUNT, 0); bundle.putParcelableArrayList(WifiAccessPointsProbe.ACCESS_POINTS, accessPoints); WifiInfo wifiInfo = wifi.getConnectionInfo(); if (wifiInfo != null) { bundle.putString(WifiAccessPointsProbe.CURRENT_BSSID, wifiInfo.getBSSID()); bundle.putString(WifiAccessPointsProbe.CURRENT_SSID, wifiInfo.getSSID()); bundle.putInt(WifiAccessPointsProbe.CURRENT_LINK_SPEED, wifiInfo.getLinkSpeed()); bundle.putInt(WifiAccessPointsProbe.CURRENT_RSSI, wifiInfo.getRssi()); } me.transmitData(context, bundle); } } }; IntentFilter filter = new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); context.registerReceiver(this._receiver, filter); } long now = System.currentTimeMillis(); synchronized (this) { long freq = Long.parseLong(prefs.getString(WifiAccessPointsProbe.FREQUENCY, Probe.DEFAULT_FREQUENCY)); if (now - this._lastCheck > freq) { WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); if (wifi.isWifiEnabled()) { this._lastCheck = now; wifi.startScan(); } else { Bundle bundle = new Bundle(); bundle.putString("PROBE", me.name(context)); bundle.putLong("TIMESTAMP", System.currentTimeMillis() / 1000); bundle.putInt(WifiAccessPointsProbe.ACCESS_POINT_COUNT, 0); bundle.putParcelableArrayList(WifiAccessPointsProbe.ACCESS_POINTS, new ArrayList<Bundle>()); bundle.putString(WifiAccessPointsProbe.CURRENT_BSSID, ""); bundle.putString(WifiAccessPointsProbe.CURRENT_SSID, ""); bundle.putInt(WifiAccessPointsProbe.CURRENT_LINK_SPEED, -1); bundle.putInt(WifiAccessPointsProbe.CURRENT_RSSI, -1); me.transmitData(context, bundle); } } } } else SanityManager.getInstance(context).addPermissionAlert(this.name(context), "android.permission.ACCESS_FINE_LOCATION", context.getString(R.string.rationale_wifi_access_probe), null); return true; } } if (this._receiver != null) { try { context.unregisterReceiver(this._receiver); } catch (RuntimeException e) { LogManager.getInstance(context).logException(e); } this._receiver = null; } return false; } private Bundle bundleForNetworksArray(Context context, ArrayList<Bundle> objects) { Bundle bundle = new Bundle(); if (objects == null) objects = new ArrayList<>(); for (Bundle value : objects) { ArrayList<String> keys = new ArrayList<>(); String key = String.format(context.getString(R.string.display_wifi_network_title), value.getString(WifiAccessPointsProbe.SSID), value.getString(WifiAccessPointsProbe.BSSID)); Bundle wifiBundle = new Bundle(); wifiBundle.putString(context.getString(R.string.display_wifi_ssid_title), value.getString(WifiAccessPointsProbe.SSID)); wifiBundle.putString(context.getString(R.string.display_wifi_bssid_title), value.getString(WifiAccessPointsProbe.BSSID)); wifiBundle.putInt(context.getString(R.string.display_wifi_frequency_title), value.getInt(WifiAccessPointsProbe.SCAN_FREQUENCY)); wifiBundle.putInt(context.getString(R.string.display_wifi_level_title), value.getInt(WifiAccessPointsProbe.LEVEL)); keys.add(context.getString(R.string.display_wifi_ssid_title)); keys.add(context.getString(R.string.display_wifi_bssid_title)); keys.add(context.getString(R.string.display_wifi_frequency_title)); keys.add(context.getString(R.string.display_wifi_level_title)); wifiBundle.putStringArrayList("KEY_ORDER", keys); bundle.putBundle(key, wifiBundle); } return bundle; } @Override public Bundle formattedBundle(Context context, Bundle bundle) { Bundle formatted = super.formattedBundle(context, bundle); @SuppressWarnings("unchecked") ArrayList<Bundle> array = (ArrayList<Bundle>) bundle.get(WifiAccessPointsProbe.ACCESS_POINTS); int count = (int) bundle.getDouble(WifiAccessPointsProbe.ACCESS_POINT_COUNT); Bundle devicesBundle = this.bundleForNetworksArray(context, array); formatted.putBundle(String.format(context.getString(R.string.display_wifi_networks_title), count), devicesBundle); formatted.putString(context.getString(R.string.display_current_ssid_title), bundle.getString(WifiAccessPointsProbe.CURRENT_SSID)); formatted.putString(context.getString(R.string.display_current_bssid_title), bundle.getString(WifiAccessPointsProbe.CURRENT_BSSID)); formatted.putInt(context.getString(R.string.display_current_speed_title), (int) bundle.getDouble(WifiAccessPointsProbe.CURRENT_LINK_SPEED)); formatted.putInt(context.getString(R.string.display_current_rssi_title), (int) bundle.getDouble(WifiAccessPointsProbe.CURRENT_RSSI)); return formatted; } @Override public void enable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(WifiAccessPointsProbe.ENABLED, true); e.commit(); } @Override public void disable(Context context) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putBoolean(WifiAccessPointsProbe.ENABLED, false); e.commit(); } @Override public String summarizeValue(Context context, Bundle bundle) { int count = (int) bundle.getDouble(WifiAccessPointsProbe.ACCESS_POINT_COUNT); return String.format(context.getResources().getString(R.string.summary_wifi_probe), count); } @Override public Map<String, Object> configuration(Context context) { Map<String, Object> map = super.configuration(context); SharedPreferences prefs = Probe.getPreferences(context); long freq = Long.parseLong(prefs.getString(WifiAccessPointsProbe.FREQUENCY, Probe.DEFAULT_FREQUENCY)); map.put(Probe.PROBE_FREQUENCY, freq); return map; } @Override public void updateFromMap(Context context, Map<String, Object> params) { super.updateFromMap(context, params); if (params.containsKey(Probe.PROBE_FREQUENCY)) { Object frequency = params.get(Probe.PROBE_FREQUENCY); if (frequency instanceof Double) { frequency = ((Double) frequency).longValue(); } if (frequency instanceof Long) { SharedPreferences prefs = Probe.getPreferences(context); Editor e = prefs.edit(); e.putString(WifiAccessPointsProbe.FREQUENCY, frequency.toString()); e.commit(); } } } @Override @SuppressWarnings("deprecation") public PreferenceScreen preferenceScreen(Context context, PreferenceManager manager) { PreferenceScreen screen = super.preferenceScreen(context, manager); screen.setTitle(this.title(context)); screen.setSummary(R.string.summary_wifi_probe_desc); CheckBoxPreference enabled = new CheckBoxPreference(context); enabled.setTitle(R.string.title_enable_probe); enabled.setKey(WifiAccessPointsProbe.ENABLED); enabled.setDefaultValue(WifiAccessPointsProbe.DEFAULT_ENABLED); screen.addPreference(enabled); FlexibleListPreference duration = new FlexibleListPreference(context); duration.setKey(WifiAccessPointsProbe.FREQUENCY); duration.setEntryValues(R.array.probe_satellite_frequency_values); duration.setEntries(R.array.probe_satellite_frequency_labels); duration.setTitle(R.string.probe_frequency_label); duration.setDefaultValue(Probe.DEFAULT_FREQUENCY); screen.addPreference(duration); return screen; } @Override public JSONObject fetchSettings(Context context) { JSONObject settings = super.fetchSettings(context); try { JSONObject frequency = new JSONObject(); frequency.put(Probe.PROBE_TYPE, Probe.PROBE_TYPE_LONG); JSONArray values = new JSONArray(); String[] options = context.getResources().getStringArray(R.array.probe_satellite_frequency_values); for (String option : options) { values.put(Long.parseLong(option)); } frequency.put(Probe.PROBE_VALUES, values); settings.put(Probe.PROBE_FREQUENCY, frequency); } catch (JSONException e) { LogManager.getInstance(context).logException(e); } return settings; } @Override public String summary(Context context) { return context.getString(R.string.summary_wifi_probe_desc); } }